package vs1;

import Bots.Bots;
import FALCON.FALCON;
import cz.cuni.amis.pogamut.base.utils.guice.AgentScoped;
import cz.cuni.amis.pogamut.ut2004.communication.messages.ItemType;
import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.StopShooting;
import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Item;
import cz.cuni.amis.pogamut.ut2004.utils.UT2004BotRunner;
import cz.cuni.amis.utils.collections.MyCollections;
import cz.cuni.amis.utils.exception.PogamutException;

import java.util.ArrayList;

@AgentScoped
public class wang_et_al extends TMP {
    public static int numState = 9 * 2;

    public wang_et_al() {
        super();
        for (int i = 0; i < agent_num; i++) {
            bots[i] = new Bots(i, numState, numAction, rw2, "bots");
            agent[i] = new FALCON(i, numState, numAction, rw1, "agent");
        }
    }

    //逻辑
    @Override                           // 重写
    public void logic() {               // 核心每个时间步做的事（分线程）
        bots[agt].step1++;                    // step++
        //if(!getNavigation().isNavigating())getNavigation().navigate(navPoints.getRandomNavPoint());
        FALCONBOT(agent, bots);      // 多智能体唯一的不同
        if (bots[agt].step1 == 1000) {            //
            bots[agt].step1 = 0;                 //
            if (agt == 0) {
                FALCON.QEpsilon -= FALCON.QEpsilonDecay;
                EVAL(agent, bots);
                System.out.println(FALCON.QEpsilon);
            }
        }
    }

    public void FALCONBOT(FALCON[] agent, Bots[] bots) {
        Learning(agent[agt], bots[agt]);        // 进行学习
        bots[agt].step++;                 // 智能体步骤数加一
        perf(agent[agt], bots[agt]);            // 利用网络选择动作
    }

    /////////////////////////////////////////////////////////////////
    ///////////////////////奖励函数   ///////////////////////////////////
    ////////////////////////////////////////////////////////////////
    @Override
    public void reward_function(Bots bots) {
        if (bots.r < 0) bots.r = 0;                                                   // 假设奖励值过大或过小将其限制在一定范围
        if (bots.r > 1) bots.r = 1;
        //bots.r = 1/(1+Math.pow(Math.E, -bots.r));                                // 理论上可以让整体更平滑的收敛，但本身不是梯度更新反而增加计算量
    }

    ////////////////////////////////////////////////////////////////
    /////////////////////// 状态   ///////////////////////////////////
    ///////////////////////////////////////////////////////////////


    //状态采样
    @Override
    public void observe(Bots bots) {

        int i = 0;
        bots.Re();                            // 将智能体的状态化为默认值
        //血量0
        bots.stat[2 * i] = (double) info.getHealth() / 100.0;     // 游戏开始后会获取生命值
        if (bots.stat[0] > 1) bots.stat[2 * i] = 1.0;
        bots.stat[1] = 1 - bots.stat[2 * i];
        i += 1;

        //子弹0，先提取智能体手上持有的武器，然后进行射击
        ItemType weaponType = weaponry.getItemTypeForId(info.getCurrentWeapon());
        if (weaponType != null)
            bots.stat[2 * i] = ((double) weaponry.getAmmo(weaponType) / (weaponry.getWeaponDescriptor(weaponType).getPriMaxAmount() + weaponry.getWeaponDescriptor(weaponType).getSecMaxAmount()));
        if (bots.stat[2 * i] > 1) bots.stat[2 * i] = 1.0;
        bots.stat[2 * i + 1] = 1 - bots.stat[2 * i];
        i += 1;

        //是否被攻击1，游戏开始后传感器会感知智能体是否会被攻击
        if (senses.isBeingDamaged()) {
            bots.stat[2 * i] = 0;
            bots.stat[2 * i + 1] = 1;
        }
        i += 1;


        //有敌人2，智能体是否能看到敌人
        if (players.canSeeEnemies()) {
            bots.stat[2 * i] = 0;
            bots.stat[2 * i + 1] = 1;
        }
        i += 1;

        if (getNavigation().isNavigating()) {
            bots.stat[2 * i] = 0;
            bots.stat[2 * i + 1] = 1;
        }
        i += 1;

        //动作状态5-8，一共有numAction个动作，采用掩码表示
        for (int j = 0; j < bots.numAction; j++) {
            if (bots.action == j) {
                bots.stat[2 * i + 2 * j] = 0;
                bots.stat[2 * i + 2 * j + 1] = 1;
            }
        }
        //i+=bots.numAction;
    }

    ////////////////////////////////////////////////////////////////
    /////////////////////// 动作   /////////////////////////////////////
    ////////////////////////////////////////////////////////////////
    static public int numAction = 4;

    //到处跑动0
    public void getRandomNavPoint() {
        bot.getBotName().setInfo("getRandomNavPoint");         // 给智能体一个名字，方便观察
        while (!getNavigation().isNavigating()) {
            getNavigation().navigate(getNavPoints().getRandomNavPoint());
        } // 如果导航点无法到达则更换新的导航点
    }


    //	public List<Item> itemsToRunAround = new CopyOnWriteArrayList<>();
//	public List<Item>  interesting = new CopyOnWriteArrayList<>();
    //拾取物品1
    public void RunAroundItems(Bots bots) {
        bot.getBotName().setInfo("RunAroundItems");         // 给智能体一个名字，方便观察

        if (getNavigation().isNavigatingToItem()) return;
        try {
            bots.interesting = new ArrayList<>();
            for (ItemType itemType : ItemType.Category.WEAPON.getTypes()) { // 所有武器目录
                if (!weaponry.hasWeapon(itemType) || weaponry.hasLowAmmoForWeapon(itemType, 1.0)) //如果没有这个武器，或者武器弹药缺少
                    bots.interesting.addAll(items.getSpawnedItems(itemType).values()); // 则可以去获得这个武器
            }
            for (ItemType itemType : ItemType.Category.ARMOR.getTypes()) { // 添加所有装甲
                bots.interesting.addAll(items.getSpawnedItems(itemType).values()); //
            }
            bots.interesting.addAll(items.getSpawnedItems(ItemType.Category.HEALTH).values()); //添加所有医疗物品
            Item item = null;
            if (bots.interesting.size() != 0) {
                item = MyCollections.getRandom(getTabooItems().filter(bots.interesting)); //随机选取一个物品
                if (item != null) getNavigation().navigate(item);
                ItemType cate_item = item.getType();

                while (!getNavigation().isNavigating()) {
                    bots.interesting.remove(item);
                    if (bots.interesting.size() == 0)
                        getNavigation().navigate(getNavPoints().getRandomNavPoint());
                    else {
                        item = MyCollections.getRandom(getTabooItems().filter(bots.interesting));
                        if (item != null) getNavigation().navigate(item);                    // 导航至目标
                    }
                }
            } else {
                getNavigation().navigate(getNavPoints().getRandomNavPoint());
            }
        } catch (Exception e) {
            getNavigation().navigate(getNavPoints().getRandomNavPoint());
        }
        bots.item = null;
    }

    //医疗2
    public void MedKit(Bots bots) {
        bot.getBotName().setInfo("MED-KIT");                           // 给智能体一个名字，方便观察
        Item item = null;

        if (bots.item != null &&
                getNavigation().isNavigatingToItem() &&
                ItemType.Category.HEALTH.getTypes().contains(bots.item.getType())) return;

        try {
            item = items.getPathNearestSpawnedItem(ItemType.Category.HEALTH); // 否则获得最近的医疗物品
            if (item != null) getNavigation().navigate(item);                                         // 如果有目标则导航至目标
            bots.interesting = new ArrayList<>();                             // 则任意一个医疗目标都行
            while (!getNavigation().isNavigating()) {             // 如果无法成功导航
                bots.interesting.remove(item);
                if (bots.interesting.size() == 0) {
                    getNavigation().navigate(getNavPoints().getRandomNavPoint());
                } else {
                    item = MyCollections.getRandom(getTabooItems().filter(bots.interesting));
                    if (item != null) getNavigation().navigate(item);
                }

            }
        } catch (Exception e) {
            getNavigation().navigate(getNavPoints().getRandomNavPoint());
        }
        bots.item = item;                    // 将导航物品设为本物品
    }

    //射击3
    public void Engage(Bots bots) {
        bot.getBotName().setInfo("stateEngage");                 // 给智能体一个名字，方便观察

        double distance;                      // 设定一个最大距离


        if (bots.enemy == null || !bots.enemy.isVisible()) {     // 如果敌人不存在或敌人看不见
            bots.enemy = players.getNearestVisiblePlayer(players.getVisibleEnemies().values());  //设定一个敌人
            if (bots.enemy != null) if (bots.enemy.getName().toCharArray()[0] == this.info.getName().toCharArray()[0]) {
                bots.enemy = null;
            }
            if (bots.enemy == null) {                            // 假设敌人不存在
                if (info.isShooting() || info.isSecondaryShooting()) {
                    getAct().act(new StopShooting());            // 就不要射击了
                }
                getNavigation().navigate(getNavPoints().getRandomNavPoint());   // 随机移动一个位置
                return;
            }
        }    // 有了敌人后

        bots.item = null;                                        // 如果看到敌人就不导航至物品了
        if (!bots.enemy.isVisible()) {                             // 如果敌人看不到
            if (info.isShooting() || info.isSecondaryShooting()) {
                getAct().act(new StopShooting());                // 就不要射击了
                bots.shooting = false;
            }
            bots.runningToPlayer = false;                        // 没用
        } else {                                                 // 如果能看到敌人
            if (shoot.shoot(weaponPrefs, bots.enemy) != null) {     // 射击敌人
                bots.shooting = true;                            // 没用
            }
        }
        distance = info.getLocation().getDistance(bots.enemy.getLocation());   // 计算与敌人的距离
        int decentDistance = Math.round(random.nextFloat() * 800) + 200;            // 一个随机距离
        if (!bots.enemy.isVisible() || !info.isShooting() || decentDistance < distance) { // 如果敌人看不到 或者 没有在射击 或者 随机距离 小于和敌人距离
            getNavigation().navigate(bots.enemy);         // 朝向对方
            if (!getNavigation().isNavigating()) getNavigation().navigate(getNavPoints().getRandomNavPoint());
        } else {                                         // 如果 看到敌人 且 在射击 且 随机距离大于与敌人距离
            getNavigation().stopNavigation();                 // 则原地射击
        }
    }

    @Override
    public void SelectVAct(Bots bots) {
        int i = 0;
        bots.vact[i] = true;  //getRandomNavPoint
        i += 1;
        bots.vact[i] = true;     //  RunAroundItems
        i += 1;
        bots.vact[i] = true;   //  MedKit
        i += 1;
        bots.vact[i] = true;   // Engage
    }

    //选择动作
    @Override
    public void DoAct(Bots bots, int action) {
        int i = 0;
        if (action == i) {
            if (info.isShooting() || info.isSecondaryShooting()) { // 停止射击
                getAct().act(new StopShooting());
            }
            getRandomNavPoint();
            return;                        // 随机移动
        }
        i += 1;
        if (action == i) {
            if (info.isShooting() || info.isSecondaryShooting()) { // 停止射击
                getAct().act(new StopShooting());
            }
            RunAroundItems(bots);
            return;                       // 跑向物品
        }
        i += 1;
        if (action == i) {
            if (info.isShooting() || info.isSecondaryShooting()) { // 停止射击
                getAct().act(new StopShooting());
            }
            MedKit(bots);
            return;                               // 加血
        }
        i += 1;
        if (action == i) {
            Engage(bots);                              // 攻击
        }
    }

    public static void main(String[] args) throws PogamutException {
        new UT2004BotRunner(wang_et_al.class, "Hunter").setMain(true).setConsoleLogging(true).startAgents(agent_num);
    }
}